#include "App_Massage.h"

#define MIN_DELAY_TIME_MS_UNIT          10  /* 最小延时时间单位, 10ms */
#define AIR_ESCAPE_TIME                 600 /* 按摩气袋放气时间 0.6s */
#define WAVE_MODEL_GROUP_LOOP_COUNT     1   /* 波浪模式每组循环次数 */
#define PULSE_MODEL_GROUP_LOOP_COUNT    1   /* 脉冲模式每组循环次数 */
#define STRETCH_MODEL_GROUP_LOOP_COUNT  3   /* 伸展模式每组循环次数 */
#define LOIN_MODEL_GROUP_LOOP_COUNT     3   /* 腰部模式每组循环次数 */
#define SHOULDER_MODEL_GROUP_LOOP_COUNT 3   /* 肩部模式每组循环次数 */

/* 阀真实点位对应PIN */
#define AM_POINT_1  SOLENOID_VALVE_INDEX_AM1
#define AM_POINT_2  SOLENOID_VALVE_INDEX_AM2
#define AM_POINT_3  SOLENOID_VALVE_INDEX_AM3
#define AM_POINT_4  SOLENOID_VALVE_INDEX_AM4
#define AM_POINT_5  SOLENOID_VALVE_INDEX_AM5
#define AM_POINT_6  SOLENOID_VALVE_INDEX_AM6
#define AM_POINT_7  SOLENOID_VALVE_INDEX_AM7
#define AM_POINT_8  SOLENOID_VALVE_INDEX_AM8
#define AM_POINT_9  SOLENOID_VALVE_INDEX_AM9
#define AM_POINT_10 SOLENOID_VALVE_INDEX_AM10

static bool App_Massage_DelayMs(uint32_t delayTime);
static bool App_Massage_ExecuteModel(const uint8_t *order, uint8_t groups, uint8_t loopCount, bool isPair);
static void App_Massage_WaveModel(void);
static void App_Massage_PulseModel(void);
static void App_Massage_StretchModel(void);
static void App_Massage_LoinModel(void);
static void App_Massage_ShoulderModel(void);

/* 按摩配置 */
Massage massageConfig = { .massageModel       = MASSAGE_MODEL_WAVE,
                          .massageGear        = MAESSAGE_GEAR_MIDDLE,
                          .isMassageOn        = false,
                          .isModelShift       = false,
                          .massageResidueTime = MASSAGE_MAX_TIME };

/* 波浪模式顺序组 */
static const uint8_t WAVE_ORDER[][2] = {
    { AM_POINT_1, AM_POINT_2 }, { AM_POINT_3, AM_POINT_4 },  { AM_POINT_5, AM_POINT_6 },
    { AM_POINT_7, AM_POINT_8 }, { AM_POINT_9, AM_POINT_10 },
};
static const uint8_t WAVE_GROUPS = sizeof(WAVE_ORDER) / sizeof(WAVE_ORDER[0]);

/* 脉冲模式顺序组 */
static const uint8_t PULSE_ORDER[] = {
    AM_POINT_1, AM_POINT_2, AM_POINT_3, AM_POINT_4, AM_POINT_5,
    AM_POINT_6, AM_POINT_7, AM_POINT_8, AM_POINT_9, AM_POINT_10,
};
static const uint8_t PULSE_GROUPS = sizeof(PULSE_ORDER) / sizeof(PULSE_ORDER[0]);

/**
 * @brief 初始化按摩模块
 *
 */
void App_Massage_Init(void)
{
    /* 初始化电磁阀 */
    Inf_SolenoidValve_Init();

    /* 初始化泵 */
    Inf_Pump_Init();

    /* 启动定时器, 记录按摩时间 */
    Driver_LPTIM16_Init();
}

/**
 * @brief 启动按摩
 *
 */
void App_Massage_Start(void)
{
    if (!massageConfig.isMassageOn) {
        return;
    }
    LOG("Massage is already on\n");

    /* 清除模式切换标志 */
    massageConfig.isModelShift = false;

    /* 打开气泵 和 电磁阀阀门开关 */
    if (!pumpIsOn) {
        Inf_Pump_Enable();
    }
    if (!solenoidValveSwitchIsOn) {
        Inf_SolenoidValve_EnableSwitch();
    }

    /* 调用对应的按摩函数 */
    switch (massageConfig.massageModel) {
    case MASSAGE_MODEL_WAVE:     App_Massage_WaveModel(); break;
    case MASSAGE_MODEL_PULSE:    App_Massage_PulseModel(); break;
    case MASSAGE_MODEL_STRETCH:  App_Massage_StretchModel(); break;
    case MASSAGE_MODEL_LOIN:     App_Massage_LoinModel(); break;
    case MASSAGE_MODEL_SHOULDER: App_Massage_ShoulderModel(); break;
    default:                     LOG("Massage model error\n"); break;
    }
}

/**
 * @brief 停止按摩
 *
 */
void App_Massage_Stop(void)
{
    LOG("App_Massage_Stop()\n");

    massageConfig.isMassageOn        = false;
    massageConfig.massageResidueTime = 0;

    /* 关闭气泵 和 电磁阀阀门开关 */
    if (pumpIsOn) {
        Inf_Pump_Disable();
    }
    if (solenoidValveSwitchIsOn) {
        Inf_SolenoidValve_DisableSwitch();
    }

    /* 关闭所有的阀 */
    for (uint8_t i = 0; i <= SOLENOID_VALVE_INDEX_AM10; ++i) {
        if (solenoidValveList[i].isEnable) {
            Inf_SolenoidValve_DeactivateSolenoidValve(&(solenoidValveList[i]));
        }
    }
}

/**
 * @brief 切换按摩模式
 *
 * @param changeWay 递增or递减
 */
void App_Massage_ModelShift(ChangeWay_e changeWay)
{
    LOG("App_Massage_ModelShift()\n");

    /* 停止按摩 */
    App_Massage_Stop();

    massageConfig.isModelShift = true;

    /* 模式切换 */
    if (changeWay == CHANGE_WAY_INCREASE) {
        ++(massageConfig.massageModel);
    }
    else if (changeWay == CHANGE_WAY_DECREASE) {
        --(massageConfig.massageModel);
    }
    else {
        LOG("App_Massage_ModelShift() changeWay value error\n");
    }

    /* 模式超出范围, 恢复到正常范围 */
    if (massageConfig.massageModel >= MASSAGE_MODEL_MAX) {
        massageConfig.massageModel = MASSAGE_MODEL_WAVE;
    }
    else if (massageConfig.massageModel < MASSAGE_MODEL_WAVE) {
        massageConfig.massageModel = (MassageModel_e)(MASSAGE_MODEL_MAX - 1);
    }

    /* 刷新按摩时间, 每次切换按摩模式时恢复到15分钟 */
    massageConfig.massageResidueTime = MASSAGE_MAX_TIME;

    /* 重新开启按摩 */
    massageConfig.isMassageOn = true;
}

/**
 * @brief 切换按摩挡位
 *
 * @param changeWay 递增or递减
 */
void App_Massage_GearShift(ChangeWay_e changeWay)
{
    LOG("App_Massage_GearShift()\n");

    if (changeWay == CHANGE_WAY_INCREASE) {
        massageConfig.massageGear += 2000;
    }
    else if (changeWay == CHANGE_WAY_DECREASE) {
        massageConfig.massageGear -= 2000;
    }

    if (massageConfig.massageGear >= MAESSAGE_GEAR_MAX) {
        massageConfig.massageGear = MAESSAGE_GEAR_LOW;
    }
    else if (massageConfig.massageGear < MAESSAGE_GEAR_LOW) {
        massageConfig.massageGear = (MassageGear_e)(MAESSAGE_GEAR_MAX - 2000);
    }
}

/* --------------------------- 私有函数  --------------------------- */

/**
 * @brief  延时函数, 专用与按摩，为了解决及时停止按摩的问题
 *
 * @param delayTime 延时时间
 * @return 若为true, 则表示延时正常, 继续执行后续按摩操作; 反之返回的结果立即停止按摩操作
 */
static bool App_Massage_DelayMs(uint32_t delayTime)
{
    uint32_t delayCount = delayTime / MIN_DELAY_TIME_MS_UNIT; // 每次延时10ms

    for (uint32_t i = 0; i < delayCount; ++i) {
        vTaskDelay(MIN_DELAY_TIME_MS_UNIT / portTICK_PERIOD_MS);

        /* 当出现以下情况时，则立即停止延时 */
        if (massageConfig.isMassageOn == false || massageConfig.isModelShift == true ||
            massageConfig.massageResidueTime == 0) {
            return false;
        }
    }

    return true;
}

/**
 * @brief 通用按摩执行函数
 *
 * @param order 顺序组（一维或二维数组）
 * @param groups 组数
 * @param loopCount 循环次数
 * @param isPair 是否为成对点位
 */
static bool App_Massage_ExecuteModel(const uint8_t *order, uint8_t groups, uint8_t loopCount, bool isPair)
{
    for (uint8_t i = 0; i < loopCount; ++i) {
        for (uint8_t j = 0; j < groups; ++j) {
            /* 充气 */
            if (isPair) {
                uint8_t pairValves[] = { order[j * 2], order[j * 2 + 1] };
                Inf_SolenoidValve_ActivateMultiValves(pairValves, 2);
            }
            else {
                Inf_SolenoidValve_ActivateSolenoidValve(&(solenoidValveList[order[j]]));
            }

            /* 充气时间 */
            if (!App_Massage_DelayMs(massageConfig.massageGear)) {
                /* 确保关闭当前开启的阀门 */
                if (isPair) {
                    uint8_t pairValves[] = { order[j * 2], order[j * 2 + 1] };
                    Inf_SolenoidValve_DeactivateMultiValves(pairValves, 2);
                }
                else {
                    Inf_SolenoidValve_DeactivateSolenoidValve(&(solenoidValveList[order[j]]));
                }
                LOG("App_Massage_ExecuteModel inflate stop\n");
                return false;
            }

            /* 泄气 */
            if (isPair) {
                uint8_t pairValves[] = { order[j * 2], order[j * 2 + 1] };
                Inf_SolenoidValve_DeactivateMultiValves(pairValves, 2);
            }
            else {
                Inf_SolenoidValve_DeactivateSolenoidValve(&(solenoidValveList[order[j]]));
            }

            /* 放气时间 */
            if (!App_Massage_DelayMs(AIR_ESCAPE_TIME)) {
                LOG("App_Massage_ExecuteModel deflate stop\n");
                return false;
            }
        }
    }

    return true;
}

/**
 * @brief 按摩--波浪模式
 *
 */
static void App_Massage_WaveModel(void)
{
    LOG("Wave model start...\n");

    App_Massage_ExecuteModel((const uint8_t *)WAVE_ORDER, WAVE_GROUPS, WAVE_MODEL_GROUP_LOOP_COUNT, true);
}

/**
 * @brief 按摩--脉冲模式
 *
 */
static void App_Massage_PulseModel(void)
{
    LOG("Pulse model start...\n");

    App_Massage_ExecuteModel(PULSE_ORDER, PULSE_GROUPS, PULSE_MODEL_GROUP_LOOP_COUNT, false);
}

/**
 * @brief 按摩--伸展模式
 *
 */
static void App_Massage_StretchModel(void)
{
    LOG("Stretch model start...\n");

    /* 伸展模式顺序组 */
    static const uint8_t STRETCH_ORDER[][2] = {
        { AM_POINT_1, AM_POINT_3 }, { AM_POINT_2, AM_POINT_4 },  { AM_POINT_3, AM_POINT_5 },
        { AM_POINT_4, AM_POINT_6 }, { AM_POINT_5, AM_POINT_7 },  { AM_POINT_6, AM_POINT_8 },
        { AM_POINT_7, AM_POINT_9 }, { AM_POINT_8, AM_POINT_10 },
    };
    static const uint8_t STRETCH_GROUPS = sizeof(STRETCH_ORDER) / sizeof(STRETCH_ORDER[0]);

    if (!App_Massage_ExecuteModel((const uint8_t *)WAVE_ORDER, WAVE_GROUPS, STRETCH_MODEL_GROUP_LOOP_COUNT,
                                  true)) {
        LOG("App_Massage_StretchModel() WAVE_ORDER stop 1\n");
        return;
    }
    if (!App_Massage_ExecuteModel(PULSE_ORDER, PULSE_GROUPS, STRETCH_MODEL_GROUP_LOOP_COUNT, false)) {
        LOG("App_Massage_StretchModel() PULSE_ORDER stop 2\n");
        return;
    }
    if (!App_Massage_ExecuteModel((const uint8_t *)STRETCH_ORDER, STRETCH_GROUPS,
                                  STRETCH_MODEL_GROUP_LOOP_COUNT, true)) {
        LOG("App_Massage_StretchModel() STRETCH_ORDER stop 3\n");
        return;
    }
}

/**
 * @brief 按摩--托背模式
 *
 */
static void App_Massage_LoinModel(void)
{
    LOG("Loin model start...\n");

    static const uint8_t LOIN_ORDER1[][2] = {
        { AM_POINT_1, AM_POINT_2 },
        { AM_POINT_3, AM_POINT_4 },
        { AM_POINT_5, AM_POINT_6 },
    };
    static const uint8_t LOIN_GROUPS1 = sizeof(LOIN_ORDER1) / sizeof(LOIN_ORDER1[0]);

    static const uint8_t LOIN_ORDER2[] = {
        AM_POINT_1, AM_POINT_2, AM_POINT_3, AM_POINT_4, AM_POINT_5, AM_POINT_6,
    };
    static const uint8_t LOIN_GROUPS2 = sizeof(LOIN_ORDER2) / sizeof(LOIN_ORDER2[0]);

    static const uint8_t LOIN_ORDER3[][2] = {
        { AM_POINT_1, AM_POINT_3 },
        { AM_POINT_2, AM_POINT_4 },
        { AM_POINT_3, AM_POINT_5 },
        { AM_POINT_4, AM_POINT_6 },
    };
    static const uint8_t LOIN_GROUPS3 = sizeof(LOIN_ORDER3) / sizeof(LOIN_ORDER3[0]);

    if (!App_Massage_ExecuteModel((const uint8_t *)LOIN_ORDER1, LOIN_GROUPS1, LOIN_MODEL_GROUP_LOOP_COUNT,
                                  true)) {
        LOG("App_Massage_LoinModel() LOIN_ORDER1 stop 1\n");
        return;
    }
    if (!App_Massage_ExecuteModel(LOIN_ORDER2, LOIN_GROUPS2, LOIN_MODEL_GROUP_LOOP_COUNT, false)) {
        LOG("App_Massage_LoinModel() LOIN_ORDER2 stop 2\n");
        return;
    }
    if (!App_Massage_ExecuteModel((const uint8_t *)LOIN_ORDER3, LOIN_GROUPS3, LOIN_MODEL_GROUP_LOOP_COUNT,
                                  true)) {
        LOG("App_Massage_LoinModel() LOIN_ORDER3 stop 3\n");
        return;
    }
}

/**
 * @brief 按摩--肩部模式
 *
 */
static void App_Massage_ShoulderModel(void)
{
    LOG("Shoulder model start...\n");

    static const uint8_t SHOULDER_ORDER1[][2] = {
        { AM_POINT_7, AM_POINT_8 },
        { AM_POINT_9, AM_POINT_10 },
    };
    static const uint8_t SHOULDER_GROUPS1 = sizeof(SHOULDER_ORDER1) / sizeof(SHOULDER_ORDER1[0]);

    static const uint8_t SHOULDER_ORDER2[] = {
        AM_POINT_7,
        AM_POINT_8,
        AM_POINT_9,
        AM_POINT_10,
    };
    static const uint8_t SHOULDER_GROUPS2 = sizeof(SHOULDER_ORDER2) / sizeof(SHOULDER_ORDER2[0]);

    static const uint8_t SHOULDER_ORDER3[][2] = {
        { AM_POINT_7, AM_POINT_9 },
        { AM_POINT_8, AM_POINT_10 },
    };
    static const uint8_t SHOULDER_GROUPS3 = sizeof(SHOULDER_ORDER3) / sizeof(SHOULDER_ORDER3[0]);

    if (!App_Massage_ExecuteModel((const uint8_t *)SHOULDER_ORDER1, SHOULDER_GROUPS1,
                                  SHOULDER_MODEL_GROUP_LOOP_COUNT, true)) {
        LOG("App_Massage_ShoulderModel() SHOULDER_ORDER1 stop 1\n");
        return;
    }
    if (!App_Massage_ExecuteModel(SHOULDER_ORDER2, SHOULDER_GROUPS2, SHOULDER_MODEL_GROUP_LOOP_COUNT,
                                  false)) {
        LOG("App_Massage_ShoulderModel() SHOULDER_ORDER2 stop 2\n");
        return;
    }
    if (!App_Massage_ExecuteModel((const uint8_t *)SHOULDER_ORDER3, SHOULDER_GROUPS3,
                                  SHOULDER_MODEL_GROUP_LOOP_COUNT, true)) {
        LOG("App_Massage_ShoulderModel() SHOULDER_ORDER3 stop 3\n");
        return;
    }
}
